﻿using System.Collections.Concurrent;

namespace BBCore.Server.Services
{
    /// <summary>
    /// 记录锁定服务类，用于管理记录的锁定状态。
    /// 该服务使用单例模式，确保在整个应用程序中共享同一套锁定记录，
    /// 以实现多用户并发访问时的记录锁定和解锁功能。
    /// </summary>
    public class RecordLockService
    {
        /// <summary>
        /// 存储记录锁定信息的并发字典。
        /// 键为记录的锁定键，通常是根据记录的唯一标识生成的字符串。
        /// 值是一个元组，包含锁定记录的用户和锁定时间戳。
        /// 使用 ConcurrentDictionary 确保在多线程环境下对字典的操作是线程安全的。
        /// </summary>
        private readonly ConcurrentDictionary<string, (string User, DateTime Timestamp)> _lockRecords = new();

        /// <summary>
        /// 为指定的记录添加锁定信息。
        /// 如果记录已经被锁定，会更新锁定用户和锁定时间戳。
        /// </summary>
        /// <param name="lockKey">记录的锁定键，用于唯一标识一条记录。</param>
        /// <param name="currentUser">当前锁定该记录的用户。</param>
        public void AddLock(string lockKey, string currentUser)
        {
            // 使用 AddOrUpdate 方法，如果键不存在则添加新的锁定记录，存在则更新锁定信息
            _lockRecords.AddOrUpdate(lockKey, (currentUser, DateTime.Now), (k, v) => (currentUser, DateTime.Now));
        }

        /// <summary>
        /// 尝试获取指定记录的锁定用户信息。
        /// </summary>
        /// <param name="lockKey">记录的锁定键，用于唯一标识一条记录。</param>
        /// <param name="user">如果记录已被锁定，输出锁定该记录的用户；否则输出空字符串。</param>
        /// <returns>如果记录已被锁定且用户信息有效，返回 true；否则返回 false。</returns>
        public bool TryGetLockUser(string lockKey, out string user)
        {
            // 尝试从并发字典中获取指定锁定键的锁定信息
            if (_lockRecords.TryGetValue(lockKey, out var value) && !string.IsNullOrEmpty(value.User))
            {
                // 如果获取到有效的锁定信息，将锁定用户赋值给 out 参数
                user = value.User;
                return true;
            }
            // 若未找到有效的锁定信息，将 out 参数赋值为空字符串
            user = string.Empty;
            return false;
        }

        /// <summary>
        /// 移除指定记录的锁定信息。
        /// </summary>
        /// <param name="lockKey">记录的锁定键，用于唯一标识一条记录。</param>
        public void RemoveLock(string lockKey)
        {
            // 尝试从并发字典中移除指定锁定键的锁定信息
            _lockRecords.TryRemove(lockKey, out _);
        }

        /// <summary>
        /// 清理过期的锁定记录。
        /// 当锁定记录的时间戳超过指定的超时时间时，将其从并发字典中移除。
        /// </summary>
        /// <param name="timeout">锁定记录的超时时间，超过该时间的锁定记录将被清理。</param>
        public void CleanExpiredLocks(TimeSpan timeout)
        {
            // 筛选出所有超时的锁定记录的键
            var expiredKeys = _lockRecords.Where(kv => DateTime.Now - kv.Value.Timestamp > timeout).Select(kv => kv.Key).ToList();
            // 遍历超时的锁定记录的键，将其从并发字典中移除
            foreach (var key in expiredKeys)
            {
                _lockRecords.TryRemove(key, out _);
            }
        }
        /// <summary>
        /// 获取所有的锁定记录。
        /// </summary>
        /// <returns>包含所有锁定记录的字典。</returns>
        public Dictionary<string, (string User, DateTime Timestamp)> GetAllLockRecords()
        {
            return new Dictionary<string, (string User, DateTime Timestamp)>(_lockRecords);
        }
    }
}
